/**
 * Tests for making sure that retried writes will wait properly for writeConcern.
 *
 * @tags: [uses_transactions, uses_prepare_transaction]
 */
import {ReplSetTest} from "jstests/libs/replsettest.js";
import {runWriteConcernRetryabilityTest} from "jstests/libs/write_concern_util.js";

const kNodes = 2;

let replTest = new ReplSetTest({nodes: kNodes});
replTest.startSet({verbose: 1});
replTest.initiate();

let priConn = replTest.getPrimary();
let secConn = replTest.getSecondary();

// Stopping replication on secondaries can take up to 5 seconds normally. Set a small oplog
// getMore timeout so the test runs faster.
assert.commandWorked(
    secConn.adminCommand({configureFailPoint: 'setSmallOplogGetMoreMaxTimeMS', mode: 'alwaysOn'}));

let lsid = UUID();

// Start at an arbitrary txnNumber.
let txnNumber = 31;

txnNumber++;
runWriteConcernRetryabilityTest(priConn,
                                secConn,
                                {
                                    insert: 'user',
                                    documents: [{_id: 10}, {_id: 30}],
                                    ordered: false,
                                    lsid: {id: lsid},
                                    txnNumber: NumberLong(txnNumber),
                                    writeConcern: {w: 'majority', wtimeout: 200},
                                },
                                kNodes);

txnNumber++;
runWriteConcernRetryabilityTest(priConn,
                                secConn,
                                {
                                    update: 'user',
                                    updates: [
                                        {q: {_id: 10}, u: {$inc: {x: 1}}},
                                    ],
                                    ordered: false,
                                    lsid: {id: lsid},
                                    txnNumber: NumberLong(txnNumber),
                                    writeConcern: {w: 'majority', wtimeout: 200},
                                },
                                kNodes);

txnNumber++;
runWriteConcernRetryabilityTest(priConn,
                                secConn,
                                {
                                    delete: 'user',
                                    deletes: [{q: {x: 1}, limit: 1}, {q: {y: 1}, limit: 1}],
                                    ordered: false,
                                    lsid: {id: lsid},
                                    txnNumber: NumberLong(txnNumber),
                                    writeConcern: {w: 'majority', wtimeout: 200},
                                },
                                kNodes);

txnNumber++;
runWriteConcernRetryabilityTest(priConn,
                                secConn,
                                {
                                    findAndModify: 'user',
                                    query: {_id: 60},
                                    update: {$inc: {x: 1}},
                                    new: true,
                                    upsert: true,
                                    lsid: {id: lsid},
                                    txnNumber: NumberLong(txnNumber),
                                    writeConcern: {w: 'majority', wtimeout: 200},
                                },
                                kNodes);

txnNumber++;
runWriteConcernRetryabilityTest(priConn,
                                secConn,
                                {
                                    commitTransaction: 1,
                                    lsid: {id: lsid},
                                    txnNumber: NumberLong(txnNumber),
                                    autocommit: false,
                                    writeConcern: {w: 'majority', wtimeout: 200},
                                },
                                kNodes,
                                'admin',
                                function(conn) {
                                    assert.commandWorked(conn.getDB('test').runCommand({
                                        insert: 'user',
                                        documents: [{_id: 80}, {_id: 90}],
                                        ordered: false,
                                        lsid: {id: lsid},
                                        txnNumber: NumberLong(txnNumber),
                                        readConcern: {level: 'snapshot'},
                                        autocommit: false,
                                        startTransaction: true
                                    }));
                                });

txnNumber++;
runWriteConcernRetryabilityTest(priConn,
                                secConn,
                                {
                                    prepareTransaction: 1,
                                    lsid: {id: lsid},
                                    txnNumber: NumberLong(txnNumber),
                                    autocommit: false,
                                    writeConcern: {w: 'majority', wtimeout: 200},
                                },
                                kNodes,
                                'admin',
                                function(conn) {
                                    assert.commandWorked(conn.getDB('test').runCommand({
                                        insert: 'user',
                                        documents: [{_id: 100}, {_id: 110}],
                                        ordered: false,
                                        lsid: {id: lsid},
                                        txnNumber: NumberLong(txnNumber),
                                        readConcern: {level: 'snapshot'},
                                        autocommit: false,
                                        startTransaction: true
                                    }));
                                });
assert.commandWorked(priConn.adminCommand({
    abortTransaction: 1,
    lsid: {id: lsid},
    txnNumber: NumberLong(txnNumber),
    autocommit: false,
    writeConcern: {w: 'majority'},
}));

txnNumber++;
runWriteConcernRetryabilityTest(priConn,
                                secConn,
                                {
                                    abortTransaction: 1,
                                    lsid: {id: lsid},
                                    txnNumber: NumberLong(txnNumber),
                                    autocommit: false,
                                    writeConcern: {w: 'majority', wtimeout: 200},
                                },
                                kNodes,
                                'admin',
                                function(conn) {
                                    assert.commandWorked(conn.getDB('test').runCommand({
                                        insert: 'user',
                                        documents: [{_id: 120}, {_id: 130}],
                                        ordered: false,
                                        lsid: {id: lsid},
                                        txnNumber: NumberLong(txnNumber),
                                        readConcern: {level: 'snapshot'},
                                        autocommit: false,
                                        startTransaction: true
                                    }));
                                    assert.commandWorked(conn.adminCommand({
                                        prepareTransaction: 1,
                                        lsid: {id: lsid},
                                        txnNumber: NumberLong(txnNumber),
                                        autocommit: false,
                                        writeConcern: {w: 'majority'},
                                    }));
                                });

txnNumber++;
assert.commandWorked(priConn.getDB('test').runCommand({
    insert: 'user',
    documents: [{_id: 140}, {_id: 150}],
    ordered: false,
    lsid: {id: lsid},
    txnNumber: NumberLong(txnNumber),
    readConcern: {level: 'snapshot'},
    autocommit: false,
    startTransaction: true
}));
const prepareTS = assert
                      .commandWorked(priConn.adminCommand({
                          prepareTransaction: 1,
                          lsid: {id: lsid},
                          txnNumber: NumberLong(txnNumber),
                          autocommit: false,
                          writeConcern: {w: 'majority'},
                      }))
                      .prepareTimestamp;
runWriteConcernRetryabilityTest(priConn,
                                secConn,
                                {
                                    commitTransaction: 1,
                                    commitTimestamp: prepareTS,
                                    lsid: {id: lsid},
                                    txnNumber: NumberLong(txnNumber),
                                    autocommit: false,
                                    writeConcern: {w: 'majority', wtimeout: 200},
                                },
                                kNodes,
                                'admin');

replTest.stopSet();